/*************************************************************************
 * Copyright 2008 David Almilli
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *      
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 **************************************************************************/
package com.tivo.hme.bananas;

import java.awt.Rectangle;
import java.lang.reflect.Field;

public class BFocusMgrPlus extends BFocusMgr {
    private Field sx1Field;
    private Field sy1Field;
    private Field sx2Field;
    private Field sy2Field;
    private Field scxField;
    private Field scyField;

    private Field bestViewField;
    private Field bestXField;
    private Field bestYField;
    
    protected BView bestView;

    protected BView lastSrc;
    protected int lastCode;

    public BFocusMgrPlus() {
        init();
    }
    
    private void init() {
        Class<?> cls = BFocusMgr.class;
        try {
            sx1Field = cls.getDeclaredField("sx1");
            sx1Field.setAccessible(true);
            
            sy1Field = cls.getDeclaredField("sy1");
            sy1Field.setAccessible(true);
            
            sx2Field = cls.getDeclaredField("sx2");
            sx2Field.setAccessible(true);
            
            sy2Field = cls.getDeclaredField("sy2");
            sy2Field.setAccessible(true);
            
            scxField = cls.getDeclaredField("scx");
            scxField.setAccessible(true);
            
            scyField = cls.getDeclaredField("scy");
            scyField.setAccessible(true);
            
            bestViewField = cls.getDeclaredField("bestView");
            bestViewField.setAccessible(true);
            
            bestXField = cls.getDeclaredField("bestX");
            bestXField.setAccessible(true);
            
            bestYField = cls.getDeclaredField("bestY");
            bestYField.setAccessible(true);
        } catch (Exception e) {
            throw new RuntimeException("Cannot gain access to one or more BFocusMgr fields.", e);
        }
    }

    @Override
    public BView followArrow(BView src, int code) {
        //
        // if this is the opposite of the previous follow,
        // return the previous src.
        //
        if (bestView == src && bestView.getID() != -1 && lastSrc.getID() != -1) {
            // some tricky code to figure out the "reverse" of the code
            // KEY_UP => KEY_DOWN, KEY_DOWN => KEY_UP, etc.
            int reverse = code + 1 - ((code % 2) * 2);
            if (reverse == lastCode) {
                return lastSrc;
            }
        }

        lastSrc = src;
        lastCode = code;

        //
        // move src to screen space
        //
        BScreen screen = src.getScreen();

        int sx1;
        int sy1;
        int sx2;
        int sy2;

        if (src instanceof PlusView) {
            Rectangle rect = ((PlusView)src).getScreenFocusBounds();
            sx1 = rect.x + screen.getX();
            sy1 = rect.y + screen.getY();
            sx2 = sx1 + rect.width;
            sy2 = sy1 + rect.height;
        } else {
            BPoint s = src.toScreen();
            sx1 = s.x + screen.getX();
            sy1 = s.y + screen.getY();
            sx2 = sx1 + src.getWidth();
            sy2 = sy1 + src.getHeight();
        }
        
        
        int scx = (sx1 + sx2) / 2;
        int scy = (sy1 + sy2) / 2;
        setSrcBounds(sx1, sy1, sx2, sy2, scx, scy);

        //
        // now walk the tree looking for candidates
        //
        clearBestView();

        walkTree(screen, 0, 0, code);
        try {
            bestView = (BView)bestViewField.get(this);
        } catch (Exception e) {
            throw new RuntimeException("Cannot gain access to BFocusMgr bestView field.", e);
        }
        return bestView;
    }
    
    protected void setSrcBounds(int sx1, int sy1, int sx2, int sy2, int scx, int scy) {
        try {
            sx1Field.set(this, sx1);
            sy1Field.set(this, sy1);
            sx2Field.set(this, sx2);
            sy2Field.set(this, sy2);
            scxField.set(this, scx);
            scyField.set(this, scy);
        } catch (Exception e) {
            throw new RuntimeException("Cannot gain access to one or more BFocusMgr srcBounds fields.", e);
        }
    }
    
    protected void clearBestView() {
        setBestView(null);
        setBestLocation(100000, 100000);
    }
    
    protected void setBestView(BView bestView) {
        try {
            bestViewField.set(this, bestView);
            this.bestView = bestView;
        } catch (Exception e) {
            throw new RuntimeException("Cannot gain access to BFocusMgr bestView field.", e);
        }
    }
    
    protected void setBestLocation(int bestX, int bestY) {
        try {
            bestXField.set(this, bestX);
            bestYField.set(this, bestY);
        } catch (Exception e) {
            throw new RuntimeException("Cannot gain access to BFocusMgr bestX or bestY fields.", e);
        }
    }

    @Override
    protected void checkForNewBest(BView view, int dx, int dy, int code) {
        super.checkForNewBest(view, dx, dy, code);
    }

    @Override
    protected void walkTree(BView view, int dx, int dy, int code) {
        super.walkTree(view, dx, dy, code);
    }

}
